#include "helper/object/std/StandardHash.h"
#include <sstream>
#include "helper/object/std/StandardRawType.h"
#include "helper/object/std/StandardString.h"
#include "helper/object/std/StandardPointer.h"
#include "helper/object/Array.h"

namespace helper {
namespace object {

StandardHash::StandardHash(){
}
StandardHash::~StandardHash(){
    freeHolderResource();
}

size_t StandardHash::size() const{
    return hash.size();
}
const Hash::Pair::second_type * StandardHash::get(const std::string  & key) const{
    auto iter = hash.find(key);
    if(iter != hash.end()){
        return &(iter->second);
    }
    return nullptr;
//    if(containsKey(key)){
//        return &(hash.at(key));
//    }
//    return nullptr;
}

bool StandardHash::containsKey(const std::string & key) const{
    return hash.find(key) != hash.end();
}

std::pair<bool, const char> StandardHash::getChar(const Pair::first_type & key) const {
    auto * vptr = get(key);
    if(vptr != nullptr && (*vptr)->isByte()){
        return std::make_pair(true, static_cast<Byte*>(*vptr)->get());
    }
    return std::make_pair(false, (const char)0);
}
std::pair<bool, const int> StandardHash::getInt(const Pair::first_type & key) const{
    auto * vptr = get(key);
    if(vptr != nullptr && (*vptr)->isInteger()){
        return std::make_pair(true, static_cast<const int>(static_cast<Integer*>(*vptr)->get()));
    }
    return std::make_pair(false, (const int)0);
}
std::pair<bool, const long long> StandardHash::getLongLong(const Pair::first_type & key) const{
    auto * vptr = get(key);
    if(vptr != nullptr && (*vptr)->isInteger()){
        return std::make_pair(true, static_cast<Integer*>(*vptr)->get());
    }
    return std::make_pair(false, (const long long)0);
}
std::pair<bool, const double> StandardHash::getDouble(const Pair::first_type & key) const {
    auto * vptr = get(key);
    if(vptr != nullptr && (*vptr)->isDouble()){
        return std::make_pair(true, static_cast<Double*>(*vptr)->get());
    }
    return std::make_pair(false, (const double)0);
}
std::pair<bool, std::string> StandardHash::getStdString(const Pair::first_type & key) const{
    String * str = getString(key);
    return (str != nullptr) ? std::make_pair(true, str->getValue()) : std::make_pair(false, "");
}
std::pair<bool, void *> StandardHash::getPointer(const Pair::first_type & key) const {
    auto * vptr = get(key);
    if(vptr != nullptr && (*vptr)->isPointer()){
        return std::make_pair(true, static_cast<Pointer*>(*vptr)->get());
    }
    return std::make_pair(false, (void *)0);
}

String * StandardHash::getString(const Pair::first_type & key) const{
    auto * vptr = get(key);
    return (vptr != nullptr && (*vptr)->isString()) ? static_cast<String*>(*vptr) : nullptr;
}
Array * StandardHash::getArray(const Pair::first_type & key) const{
    auto * vptr = get(key);
    return (vptr != nullptr && (*vptr)->isArray()) ? static_cast<Array*>(*vptr) : nullptr;
}
Hash * StandardHash::getHash(const Pair::first_type & key) const {
    auto * vptr = get(key);
    return (vptr != nullptr && (*vptr)->isHash()) ? static_cast<Hash*>(*vptr) : nullptr;
}


int StandardHash::insert(const std::string & key, const Hash::Pair::second_type & o){
    if(o != nullptr){
        return hash.insert(HashImp::value_type(key, o)).second ? 0 : -1;
    }
    return -1;
}
template<typename T> 
static int insertOrFreeObject(StandardHash * current, const std::string & key, T * obj){
    int ret = current->insert(key, obj);
    if (0 == ret)
        return 0;
    delete obj;
    return -1;
}
int StandardHash::insertPointer(const Pair::first_type & key, const void * ptr) {
    return insertOrFreeObject(this, key, new StandardPointer(ptr));
}
int StandardHash::insert(const Pair::first_type & key, const char ch) {
    return insertOrFreeObject(this, key, new StandardByte(ch));
}
int StandardHash::insert(const Pair::first_type & key, const int i){
    return insertOrFreeObject(this, key, new StandardInteger(i));
}
int StandardHash::insert(const Pair::first_type & key, const long long ll) {
    return insertOrFreeObject(this, key, new StandardInteger(ll));
}
int StandardHash::insert(const Pair::first_type & key, const double dbl) {
    return insertOrFreeObject(this, key, new StandardDouble(dbl));
}
int StandardHash::insert(const Pair::first_type & key, const char * str){
    return insertOrFreeObject(this, key, new StandardString(str));
}
int StandardHash::insert(const Pair::first_type & key, const char * buffer , size_t len){
    return insertOrFreeObject(this, key, new StandardString(buffer, len));
}
int StandardHash::insert(const Pair::first_type & key, const char * buffer , size_t offset, size_t len){
    return insertOrFreeObject(this, key, new StandardString(buffer, offset, len));
}
int StandardHash::insert(const Pair::first_type & key, size_t n, char ch){
    return insertOrFreeObject(this, key, new StandardString(n, ch));
}

int StandardHash::insert(const std::string & key, const std::string & value){
    return insertOrFreeObject(this, key, new StandardString(value));
}

void StandardHash::freeHolderResource(){
    for(auto iter = hash.begin(); iter != hash.end(); ++iter){
        Object * ptr = iter->second;
        if(ptr != nullptr){
            delete ptr;
            hash[iter->first] = nullptr;
        }
    }
}
std::shared_ptr<Iterator<Hash::Pair>> StandardHash::iterator() const{
    return std::make_shared<StandardHashIterator>(*this);
}
Hash::Pair::second_type StandardHash::remove(const Hash::Pair::first_type & key) {
    auto iter = hash.find(key); 
    if(iter != hash.end()){
        Hash::Pair::second_type old = iter->second;
        hash.erase(iter);
        return old;
    }
    return nullptr;
}
Hash::Pair::second_type StandardHash::set(const Hash::Pair::first_type & key, Hash::Pair::second_type & value){
    Hash::Pair::second_type old = nullptr; 
    auto iter = hash.find(key);
    if(iter != hash.end()){
        old = iter->second;
        hash.erase(iter);
    }
    insert(key, value);
    return old;
}

std::string StandardHash::toString() const{
    std::ostringstream oss;
    oss << "StandardHash:[" << hash.size() << "] : ";
    for(auto iter = hash.begin(); iter != hash.end(); ++iter){
        std::string val = (iter->second != nullptr) ? iter->second->toString() : "nullptr";
        oss << iter->first << ":" << val << ", ";
    }
    return oss.str();
}

bool StandardHash::equals(const Object & obj) const {
    if(this == &obj) return true;

    if(obj.isHash()){
        auto & otherHash = static_cast<const StandardHash&>(obj);
        if(size() == otherHash.size()){
            for(auto & pair : hash){
                auto * otherPair = otherHash.get(pair.first); //get ptr to ValueType
                if(otherPair == nullptr || *otherPair == nullptr 
                    || pair.second == nullptr 
                    || !(pair.second->equals(**otherPair))){
                        return false;
                    }
            }
            return true;
        }
        return false;//diff size
    }
    return false; //not hash 
}
Object * StandardHash::copy() const {
    StandardHash * newHash = new StandardHash();
    auto iter = iterator();
    while (iter->hasNext()) {
        const Hash::Pair & p = iter->next();
        if (p.second != nullptr) {
            newHash->insert(p.first, p.second->copy());
        }
        else {
            delete newHash;
            return nullptr;
        }
    }
    return newHash;
}

void StandardHash::clear() {
    freeHolderResource();
    hash.clear();
}

StandardHashIterator::StandardHashIterator(Hash & hash)
: hashRef(hash)
{
    StandardHash & sh = (StandardHash &)(hashRef);
    iter = sh.hash.begin();
    end = sh.hash.end();
}

StandardHashIterator::StandardHashIterator(const Hash & hash)
: hashRef(hash)
{
    StandardHash & sh = (StandardHash &)(hashRef);
    iter = sh.hash.begin();
    end = sh.hash.end();
}

StandardHashIterator::~StandardHashIterator(){
}

bool StandardHashIterator::hasNext() const{
    return iter != end;
}

const Hash::Pair & StandardHashIterator::next(){
    if(iter != end){
        return *(iter++);
    }
    return getNullObjRef();
}

} // end of namespace object
} // end of namespace helper
